import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import os
import subprocess
import tempfile
import shutil
import sys
import threading
import signal  # 用于Unix/Linux系统的信号处理
from datetime import datetime
import platform


def get_platform():
    system = platform.system()
    if system == "Windows":
        return "Windows"
    elif system == "Darwin":  # macOS的系统标识是Darwin
        return "macOS"
    elif system == "Linux":
        return "Linux"
    else:
        return "Other"


# 用于资源管理的类
class ResourceManager:
    def __init__(self):
        self.temp_dir = tempfile.mkdtemp()

    def extract_afs_tool(self):
        """从内部资源中提取UnrealAndroidFileTool.exe到临时目录"""

        system = platform.system()

        if system == "Windows":
            tool_name = "UnrealAndroidFileTool.exe"
        elif system == "Darwin":
            tool_name = "UnrealAndroidFileTool"  # macOS的可执行文件没有.exe后缀
        else:
            self.log("错误: 不支持的操作系统")
            return None

        # 如果是打包后的exe，就从内部资源读取
        if hasattr(sys, "_MEIPASS"):
            # 在打包环境中，资源文件在sys._MEIPASS目录中
            meipass_path = sys._MEIPASS
            afs_source = os.path.join(meipass_path, tool_name)
            if os.path.exists(afs_source):
                afs_path = os.path.join(self.temp_dir, tool_name)
                shutil.copy2(afs_source, afs_path)
                return afs_path
        else:
            # 在开发环境中，直接使用当前目录的UnrealAndroidFileTool
            dev_afs_path = os.path.join(os.path.dirname(__file__), tool_name)
            if os.path.exists(dev_afs_path):
                return dev_afs_path

        return None

    def cleanup(self):
        """清理临时文件"""
        if os.path.exists(self.temp_dir):
            shutil.rmtree(self.temp_dir)


class APKInstallerApp:
    def __init__(self, root):
        self.root = root
        self.root.title("APK安装工具")

        self.platform_name = get_platform()
        print(f"当前平台: {self.platform_name}")

        # 设置DPI感知（特别是在Windows上）
        try:
            from ctypes import windll

            windll.shcore.SetProcessDpiAwareness(1)
        except:
            pass

        # 统一的窗口大小，让系统自动处理缩放
        self.root.geometry("1200x800")  # 使用适中的尺寸

        # 或者根据平台使用不同的尺寸
        if platform.system() == "Darwin":
            self.root.geometry("1100x750")  # macOS上的尺寸
        else:
            self.root.geometry("1440x1050")  # Windows上的尺寸

        self.root.resizable(False, False)

        self.worker_running = False
        self.current_process = None  #
        self.resource_manager = ResourceManager()
        self.afs_tool_path = self.resource_manager.extract_afs_tool()

        self.create_widgets()

        # self.check_tools()

        self.log("欢迎使用APK安装工具")

    def create_widgets(self):
        """创建界面组件"""
        # 主框架
        main_frame = ttk.Frame(self.root, padding="0")
        main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

        # 配置行列权重
        self.root.columnconfigure(0, weight=1)
        self.root.rowconfigure(0, weight=1)
        main_frame.columnconfigure(0, weight=1)

        # 横幅区域
        try:
            if hasattr(sys, "_MEIPASS"):
                banner_path = os.path.join(sys._MEIPASS, "res/banner2.png")
            else:
                banner_path = "res/banner2.png"

            if os.path.exists(banner_path):
                banner_image = tk.PhotoImage(file=banner_path)
                banner_label = ttk.Label(main_frame, image=banner_image)
                banner_label.image = banner_image
                banner_label.grid(row=0, column=0, pady=(0, 0))
            else:
                # 回退到文字
                banner_label = ttk.Label(
                    main_frame,
                    text="APK安装工具",
                    font=("Arial", 18, "bold"),
                    foreground="blue",
                )
                banner_label.grid(row=0, column=0, pady=(0, 20))

        except Exception as e:
            # 如果加载图片失败，使用文字代替
            banner_label = ttk.Label(
                main_frame,
                text="APK安装工具",
                font=("Arial", 18, "bold"),
                foreground="blue",
            )
            banner_label.grid(row=0, column=0, pady=(0, 20))

        # 说明文字
        info_text = "选择包含Android_ASTC目录的文件夹，程序将自动安装其中的APK文件，并使用afs工具推送paks目录中的.pak文件到设备。"
        info_label = ttk.Label(main_frame, text=info_text, wraplength=1000)
        info_label.grid(row=1, column=0, pady=(10, 20))

        # 目录选择区域
        dir_frame = ttk.Frame(main_frame)
        dir_frame.grid(row=2, column=0, pady=(0, 20), sticky=(tk.W, tk.E), padx=20)
        dir_frame.columnconfigure(0, weight=1)

        # 目录路径文本框
        self.dir_var = tk.StringVar(value="未选择目录")
        dir_entry = ttk.Entry(dir_frame, textvariable=self.dir_var, state="readonly")
        dir_entry.grid(row=0, column=0, padx=(0, 10), sticky=(tk.W, tk.E))

        # 选择目录按钮
        self.select_button = ttk.Button(
            dir_frame, text="选择目录", command=self.select_directory
        )
        self.select_button.grid(row=0, column=1)

        # 开始安装按钮
        self.configure_styles()
        self.install_button = ttk.Button(
            main_frame,
            text="开始安装",
            command=self.start_installation,
            style="GreenLarge.TButton",
        )
        self.install_button.grid(
            row=3, column=0, pady=(0, 10), sticky=(tk.W, tk.E), padx=20
        )

        # 复选框区域 - 跳过安装选项
        checkbox_frame = ttk.Frame(main_frame)
        checkbox_frame.grid(row=4, column=0, pady=(0, 20), sticky=(tk.W, tk.E), padx=20)

        self.skip_install_var = tk.BooleanVar(value=False)
        self.skip_checkbox = ttk.Checkbutton(
            checkbox_frame,
            text="跳过安装步骤，直接推送Pak文件",
            variable=self.skip_install_var,
            command=self.update_install_button_text,
        )
        self.skip_checkbox.grid(row=0, column=0, sticky=tk.W)

        # 进度条
        self.progress = ttk.Progressbar(main_frame, mode="determinate")
        self.progress.grid(row=5, column=0, pady=(0, 10), sticky=(tk.W, tk.E), padx=20)
        self.progress.grid_remove()

        # 日志区域
        log_frame = ttk.LabelFrame(main_frame, text="操作日志", padding="8")
        log_frame.grid(
            row=6, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(0, 10), padx=20
        )
        log_frame.columnconfigure(0, weight=1)
        log_frame.rowconfigure(0, weight=1)

        # 创建日志文本框 - 设置行距和只读属性
        self.log_text = tk.Text(
            log_frame,
            height=8,
            wrap=tk.WORD,
            spacing1=3,  # 段前间距
            spacing2=2,  # 行间距
            spacing3=3,  # 段后间距
            state="disabled",  # 初始为禁用状态（不可编辑）
            font=("Consolas", 10),
        )  # 使用等宽字体更美观

        # 配置滚动条
        scrollbar = ttk.Scrollbar(
            log_frame, orient="vertical", command=self.log_text.yview
        )
        self.log_text.configure(yscrollcommand=scrollbar.set)

        # 布局
        self.log_text.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))

        # 状态标签
        self.status_var = tk.StringVar(value="就绪")
        status_label = ttk.Label(
            main_frame, textvariable=self.status_var, anchor="e"
        )  # 文字右对齐
        status_label.grid(row=7, column=0, pady=(5, 10), sticky=(tk.E,), padx=20)

        # 配置权重
        main_frame.rowconfigure(6, weight=1)

    def configure_styles(self):
        """配置样式"""
        style = ttk.Style()

        # 绿色大按钮样式
        if platform.system() == "Darwin":
            # macOS: 使用颜色和padding
            style.configure(
                "GreenLarge.TButton",
                font=("Arial", 14, "bold"),
                background="#4CAF50",
                foreground="white",
                padding=(0, 25),
            )
        else:
            # Windows: 使用标准样式
            style.configure(
                "GreenLarge.TButton",
                font=("Arial", 14, "bold"),
                background="#4CAF50",
                foreground="white",
                padding=(0, 15),
            )

    def check_tools(self):
        """检查必要的工具是否可用"""
        # 检查adb
        try:
            result = subprocess.run(
                "adb version", shell=True, capture_output=True, text=True, timeout=5
            )
            if result.returncode != 0:
                self.log("警告: 未找到adb工具或无法运行")
                self.log("请确保已安装Android SDK并配置了环境变量")
        except:
            self.log("警告: 检查adb时发生错误")

        # 检查afs工具
        if not self.afs_tool_path:
            self.log("错误: 未找到UnrealAndroidFileTool工具")
            self.select_button.config(state="disabled")

    def log(self, message):
        # 启用文本框进行编辑
        self.log_text.config(state="normal")

        # 添加时间戳
        timestamp = datetime.now().strftime("%H:%M:%S")
        log_message = f"[{timestamp}] {message}\n"

        # 插入日志信息
        self.log_text.insert(tk.END, log_message)

        # 滚动到最后
        self.log_text.see(tk.END)

        # 禁用文本框防止编辑
        self.log_text.config(state="disabled")

        # 更新界面
        self.root.update_idletasks()

    def select_directory(self):
        directory = filedialog.askdirectory(title="选择包含Android-ASTC目录的文件夹")
        if not directory:
            return

        self.dir_var.set(directory)
        self.log(f"选择的目录: {directory}")

    def update_install_button_text(self):
        """更新开始按钮的文本"""
        if self.skip_install_var.get():
            self.install_button.config(text="开始推送")
        else:
            self.install_button.config(text="开始安装")

    def start_installation(self):
        selected_dir = self.dir_var.get()
        if (
            not selected_dir
            or selected_dir == "请选择目录..."
            or selected_dir == "未选择目录"
        ):
            messagebox.showwarning("警告", "请先选择目录！")
            return

        if not os.path.exists(selected_dir):
            messagebox.showerror("错误", "选择的目录不存在！")
            return

        # 禁用按钮防止重复点击
        self.set_buttons_state(False)

        # 检查是否跳过安装
        if self.skip_install_var.get():
            self.status_var.set("跳过安装，开始推送文件...")
            thread = threading.Thread(target=self.push_paks_only, args=(selected_dir,))
        else:
            thread = threading.Thread(
                target=self.process_directory, args=(selected_dir,)
            )

        thread.daemon = True
        thread.start()

    def set_buttons_state(self, enabled):
        """设置按钮状态"""
        state = "normal" if enabled else "disabled"
        self.select_button.config(state=state)
        self.install_button.config(state=state)
        self.skip_checkbox.config(state=state)

    def process_directory(self, directory):
        try:
            self.worker_running = True

            self.select_button.config(state="disabled")
            self.progress.grid()
            self.status_var.set("正在处理...")

            temp_work_dir = tempfile.mkdtemp()
            self.log(f"创建临时工作目录: {temp_work_dir}")

            # 查找Install_开头的bat脚本文件
            bat_files = []
            for root, dirs, files in os.walk(directory):
                for file in files:
                    if file.startswith("Install_") and file.endswith(".bat"):
                        bat_files.append(os.path.join(root, file))

            if not bat_files:
                messagebox.showerror(
                    "错误", f"在目录及子目录中未找到Install_开头的bat脚本文件"
                )
                self.log("错误: 未找到Install_*.bat文件")
                self.set_buttons_state(True)
                shutil.rmtree(temp_work_dir)  # 清理临时目录
                return

            # self.log(f"找到bat脚本文件: {', '.join([os.path.basename(f) for f in bat_files])}")

            # 使用第一个bat脚本文件
            bat_path = bat_files[0]
            bat_filename = os.path.basename(bat_path)
            # self.log(f"选择bat脚本文件: {bat_path}")

            # 从脚本文件名提取目标文件名
            target_name = bat_filename[8:-4]  # 去掉"Install_"和".bat"
            # self.log(f"提取的目标文件名: {target_name}")

            # 查找目标apk文件
            apk_path = None

            # 先查当前目录
            current_dir_apk = os.path.join(
                os.path.dirname(bat_path), f"{target_name}.apk"
            )
            if os.path.exists(current_dir_apk):
                apk_path = current_dir_apk
            else:
                # 查找目录A及子目录
                for root, dirs, files in os.walk(directory):
                    for file in files:
                        if file == f"{target_name}.apk":
                            apk_path = os.path.join(root, file)
                            break
                    if apk_path:
                        break

            if not apk_path:
                messagebox.showerror(
                    "错误", f"未找到与脚本对应的APK文件: {target_name}.apk"
                )
                self.log(f"错误: 未找到目标APK文件 {target_name}.apk")
                shutil.rmtree(temp_work_dir)  # 清理临时目录
                self.set_buttons_state(True)
                return

            # 复制文件到临时目录
            self.status_var.set("正在处理安装流程...")
            # self.progress['value'] = 25

            # 复制bat脚本
            temp_bat_path = os.path.join(temp_work_dir, bat_filename)
            shutil.copy2(bat_path, temp_bat_path)
            # self.log(f"复制bat脚本到: {temp_bat_path}")

            # 复制apk文件
            temp_apk_path = os.path.join(temp_work_dir, f"{target_name}.apk")
            shutil.copy2(apk_path, temp_apk_path)
            # self.log(f"复制APK文件到: {temp_apk_path}")

            # 在临时目录中执行bat脚本
            self.status_var.set("正在执行安装脚本...")
            # self.progress['value'] = 50
            self.log(f"执行安装脚本: {temp_bat_path}")

            try:
                original_cwd = os.getcwd()
                os.chdir(temp_work_dir)

                # 创建子进程，设置新的进程组
                creationflags = 0
                if os.name == "nt":  # Windows
                    creationflags = subprocess.CREATE_NEW_PROCESS_GROUP
                else:  # Unix/Linux
                    creationflags = os.setsid

                # 保存进程引用
                self.current_process = subprocess.Popen(
                    f'"{temp_bat_path}"',
                    shell=True,
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE,
                    text=True,
                )
                # 实时读取输出
                while True:
                    output = self.current_process.stdout.readline()
                    if output == "" and self.current_process.poll() is not None:
                        break
                    if output:
                        cleaned_output = output.strip()
                        if cleaned_output:
                            self.log(f"{cleaned_output}")
                            self.root.update_idletasks()

                # 获取返回码
                return_code = self.current_process.wait()

                # 切换回原目录
                os.chdir(original_cwd)

                if return_code != 0:
                    stderr_output = self.current_process.stderr.read()
                    error_msg = f"bat脚本执行失败: {stderr_output if stderr_output else '未知错误'}"
                    self.log(error_msg)
                    messagebox.showerror("错误", error_msg)
                    shutil.rmtree(temp_work_dir)
                    self.set_buttons_state(True)
                    return

            except subprocess.TimeoutExpired:
                error_msg = "bat脚本执行超时"
                self.log(error_msg)
                messagebox.showerror("错误", error_msg)
                shutil.rmtree(temp_work_dir)  # 清理临时目录
                self.set_buttons_state(True)
                return
            except Exception as e:
                error_msg = f"执行脚本时发生错误: {str(e)}"
                self.log(error_msg)
                messagebox.showerror("错误", error_msg)
                shutil.rmtree(temp_work_dir)  # 清理临时目录
                self.set_buttons_state(True)
                return

            # 安装完成后推送pak文件
            self.status_var.set("安装完成，准备推送文件...")
            # self.progress['value'] = 75
            self.push_paks_to_device(directory)

            # 清理临时目录
            shutil.rmtree(temp_work_dir)
            # self.log("清理临时工作目录完成")

        except Exception as e:
            self.log(f"处理过程中发生错误: {str(e)}")
            messagebox.showerror("错误", f"处理过程中发生错误: {str(e)}")
            self.set_buttons_state(True)
            # 确保临时目录被清理
            if "temp_work_dir" in locals() and os.path.exists(temp_work_dir):
                shutil.rmtree(temp_work_dir)
        finally:
            # 清除任务运行标志
            self.worker_running = False
            self.current_process = None
            # 恢复UI状态
            self.select_button.config(state="normal")
            self.progress.grid_remove()
            self.set_buttons_state(True)

    def push_paks_only(self, directory):
        """只推送文件，跳过安装步骤"""
        try:
            # 设置任务运行标志
            self.worker_running = True
            self.select_button.config(state="disabled")
            self.install_button.config(state="disabled")
            self.skip_checkbox.config(state="disabled")
            self.progress.grid()
            self.progress["value"] = 0
            self.status_var.set("正在准备推送文件...")

            # 直接调用推送函数
            self.push_paks_to_device(directory)

        except Exception as e:
            self.log(f"推送文件过程中发生错误: {str(e)}")
            messagebox.showerror("错误", f"推送文件过程中发生错误: {str(e)}")
            self.set_buttons_state(True)
        finally:
            # 清除任务运行标志
            self.worker_running = False
            self.select_button.config(state="normal")
            self.install_button.config(state="normal")
            self.skip_checkbox.config(state="normal")
            self.progress.grid_remove()
            self.set_buttons_state(True)

    def push_paks_to_device(self, directory):
        """推送pak文件到设备"""
        try:
            # 设置任务运行标志
            self.worker_running = True
            self.status_var.set("正在准备推送Pak文件...")
            # self.progress['value'] = 75

            # 查找directory目录及其子目录中的Paks目录
            paks_dir = None
            for root, dirs, files in os.walk(directory):
                if "Paks" in dirs:
                    paks_dir = os.path.join(root, "Paks")
                    # self.log(f"找到Paks目录: {paks_dir}")
                    break

            if not paks_dir:
                messagebox.showerror("错误", f"在目录及子目录中未找到Paks文件夹")
                self.log("错误: 未找到Paks目录")
                self.set_buttons_state(True)
                return

            # 统计paks目录下.pak文件的数量
            pak_files = [f for f in os.listdir(paks_dir) if f.endswith(".pak")]
            pak_total = len(pak_files)

            if pak_total == 0:
                messagebox.showerror("错误", "在Paks目录中未找到.pak文件")
                self.log("错误: 未找到.pak文件")
                self.set_buttons_state(True)
                return

            self.status_var.set("正在推送PAK文件...")
            # self.progress['value'] = 80

            push_cmd = (
                f'"{self.afs_tool_path}" -p com.YHKT.FY -k F8508920421E535D027A3093D17E9EF8 '
                f'push -c "{paks_dir}" /sdcard/Android/data/com.YHKT.FY/files/UnrealGame/FY/FY/Saved/Paks/'
            )
            self.log(f"执行推送命令: {push_cmd}")

            # 创建子进程，设置新的进程组
            creationflags = 0
            if os.name == "nt":  # Windows
                creationflags = subprocess.CREATE_NEW_PROCESS_GROUP
            else:  # Unix/Linux
                creationflags = os.setsid

            self.current_process = subprocess.Popen(
                push_cmd,
                shell=True,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                text=True,
                bufsize=1,
                universal_newlines=True,
            )

            # 实时读取输出
            self.log(f"Paks文件数: {pak_total}")
            pak_count = 0
            while True:
                output = self.current_process.stdout.readline()
                if output == "" and self.current_process.poll() is not None:
                    break
                if output:
                    cleaned_output = output.strip()
                    if cleaned_output:
                        if "->" in cleaned_output:
                            result = cleaned_output.split("->")[1]
                            self.progress["value"] = result.split(".")[0]
                        else:
                            if "1> Writing" in cleaned_output:
                                pak_count = pak_count + 1
                                self.log(f"{pak_total}/{pak_count} {cleaned_output}")
                        self.root.update_idletasks()

            # 获取返回码
            return_code = self.current_process.wait()

            if return_code != 0:
                error_msg = f"推送失败，返回码: {return_code}"
                self.log(error_msg)
                messagebox.showerror("错误", error_msg)
                self.set_buttons_state(True)
                return

            # 推送完成
            self.progress["value"] = 100
            self.status_var.set("推送完成")
            self.log("PAK文件推送成功！")

            # 完成后关闭程序
            messagebox.showinfo("成功", "PAK文件推送成功！")
            # self.on_closing(True)

        except subprocess.TimeoutExpired:
            error_msg = "推送文件超时，请检查设备连接"
            self.log(error_msg)
            messagebox.showerror("错误", error_msg)
            self.set_buttons_state(True)
        except Exception as e:
            error_msg = f"推送文件时发生错误: {str(e)}"
            self.log(error_msg)
            messagebox.showerror("错误", error_msg)
            self.set_buttons_state(True)
        finally:
            # 清除任务运行标志
            self.worker_running = False
            self.current_process = None
            self.set_buttons_state(True)

    def on_closing(self, finish=False):
        """程序关闭时的处理"""
        # 如果正常完成直接退出
        if finish:
            self.force_terminate_process()
        else:
            # 检查是否有正在进行的任务
            if hasattr(self, "worker_running") and self.worker_running:
                result = messagebox.askyesno(
                    "确认退出",
                    "有任务正在执行，强制终止可能会造成数据不完整。\n是否强制终止所有任务并退出程序？",
                    icon=messagebox.WARNING,
                )
                if not result:  # 用户选择取消
                    return
                else:
                    self.force_terminate_process()

        # 清理资源并退出
        self.resource_manager.cleanup()
        self.root.destroy()

    def force_terminate_process(self):
        """强制终止当前进程及其所有子进程"""
        if self.current_process:
            try:
                if os.name == "nt":  # Windows系统
                    pid = self.current_process.pid
                    # 使用taskkill强制终止整个进程树
                    subprocess.run(
                        f"taskkill /F /T /PID {pid}",
                        shell=True,
                        stdout=subprocess.PIPE,
                        stderr=subprocess.PIPE,
                    )
                else:  # Unix/Linux系统, 发送信号终止进程，暂时没支持，没调试过，顺手写的
                    os.killpg(os.getpgid(self.current_process.pid), signal.SIGTERM)

                self.log("已强制终止所有运行中的进程")
            except Exception as e:
                self.log(f"终止进程时发生错误: {str(e)}")


def main():
    root = tk.Tk()
    app = APKInstallerApp(root)
    root.protocol("WM_DELETE_WINDOW", app.on_closing)
    root.mainloop()


if __name__ == "__main__":
    main()
